home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 March / Macworld (1998-03) (Disk 1).dmg / Shareware World / Utilities / Text Processing / Alpha / Tcl / SystemCode / textFill.tcl < prev    next >
Encoding:
Text File  |  1997-12-05  |  12.8 KB  |  458 lines  |  [TEXT/ALFA]

  1. ####################################################################
  2. # Much by Vince Darley.
  3. #                                    created: 26/11/96 {7:08:34 pm} 
  4. #                                last update: 16/5/96 
  5. #  Author: Vince Darley
  6. #  E-mail: <mailto:vince@das.harvard.edu>
  7. #    mail: Division of Applied Sciences, Harvard University
  8. #          Oxford Street, Cambridge MA 02138, USA
  9. #     www: <http://www.fas.harvard.edu/~darley/>
  10. #  
  11. ####################################################################
  12.  
  13. ## 
  14.  # Here's a    brief explanation of the smart fillParagraph routines
  15.  # 
  16.  # 'fillParagraph'
  17.  #       If there's a    selection, then    fill all paragraphs    in that
  18.  #       selection. If not then fill the paragraph surrounding the
  19.  #       insertion point.    The    definition of a    'paragraph'    may    be
  20.  #       mode    dependent (see paraStart, paraFinish)
  21.  #       
  22.  # 'fillOneParagraph'
  23.  #       Fills the single    paragraph surrounding the insertion    point.
  24.  #       If called with parameter    '0', it    doesn't    bother to remember
  25.  #       where the insertion point was, which    makes multiple paragraph
  26.  #       fills quicker when called by    'fillParagraph'
  27.  #       
  28.  # 'rememberWhereYouAre'
  29.  #       Given the start of a    paragraph and the point    to remember,
  30.  #       this    creates    a record stored    in '__g_remember_pos' so that
  31.  #       the following function can find that    spot later,    even after
  32.  #       the paragraph has had space/tabs/new-lines meddled with.
  33.  #       
  34.  # 'goBackToWhereYouWere'
  35.  #       Given the beginning and end of a    selection, where the beginning
  36.  #       corresponds to a    previous call of 'rememberWhereYouAre',    this
  37.  #       procedure will move the insertion point to the correct place.
  38.  #       
  39.  # 'texParaCommands'
  40.  #       A variable containing the bulk of a regexp for paragraph
  41.  #       indicators in 'TeX' mode.
  42.  #       
  43.  # 'paraStart'
  44.  #       Finds the start of the paragraph    containing the insertion point.
  45.  #       
  46.  # 'paraFinish'
  47.  #       Finds the end of    the    paragraph containing the insertion point.
  48.  ##
  49.     
  50. proc fillParagraph {} {
  51.     if {[getPos] == [selEnd]} {
  52.         fillOneParagraph
  53.     } else {    
  54.         set start [getPos]
  55.         set end [selEnd]
  56.         set p $start
  57.         while { $p < $end && $p < [maxPos]} {
  58.             goto $p
  59.             set p [fillOneParagraph 0]
  60.         }
  61.         goto $start
  62.     }
  63. }
  64.  
  65. proc rememberWhereYouAre { startPara pos } {
  66.     global __g_remember_str
  67.     set srem [expr $pos -20 < $startPara ? $startPara : $pos - 20]
  68.     set __g_remember_str [quote::Regfind [getText $srem $pos ]]
  69.     regsub -all "\[ \t\r\]+" $__g_remember_str {[ \t\r]+} __g_remember_str
  70. }
  71.  
  72. proc goBackToWhereYouWere { start end } {
  73.     global __g_remember_str
  74.     if { $__g_remember_str != "" } {
  75.         regexp -indices ".*(${__g_remember_str}).*" [getText $start $end] wholematch submatch
  76.         set p [expr [info exists submatch] ? \
  77.             [expr $start + 1 + [lindex $submatch 1]] : $end]
  78.         goto [expr $p >= $end ? $end -1 : $p]
  79.     } else {
  80.         goto $start
  81.     }
  82. }
  83.  
  84. ## 
  85.  # -------------------------------------------------------------------------
  86.  #     
  87.  #    "getLeadingIndent" --
  88.  #    
  89.  #     Find the indentation of the line containing 'pos',    and    convert    it
  90.  #     to    a minimal form of tabs followed    by spaces.    If 'size'
  91.  #     is    given, then    the    variable of    that name is set to    the    length of
  92.  #     the indent. Similarly 'halftab' can be set to half a tab.
  93.  # -------------------------------------------------------------------------
  94.  ##
  95. proc getLeadingIndent { pos {size ""} {halftab ""} } {
  96.     # get the leading whitespace of the current line
  97.     set res [search -s -n -f 1 -r 1 "^\[ \t\]*" [lineStart $pos]]
  98.     
  99.     # convert it to minimal form: tabs then spaces, stored in 'front'
  100.     getWinInfo a
  101.     set sp [string range "              " 1 $a(tabsize) ]
  102.     regsub -all "($sp| +\t)" [eval getText $res] "\t" front
  103.     if { $size != "" } {
  104.         upvar $size ind
  105.         # get the length of the indent
  106.         regsub -all "\t" $front $sp lfront
  107.         set ind [string length $lfront]
  108.     }
  109.     if { $halftab != "" } {
  110.         upvar $halftab ht
  111.         # get the length of half a tab
  112.         set ht [string range "            " 1 [expr $a(tabsize)/2]]
  113.     }
  114.     
  115.     return $front
  116. }
  117.  
  118. ## 
  119.  # -------------------------------------------------------------------------
  120.  # 
  121.  # "fillOneParagraph" --
  122.  # 
  123.  #  Fixes: won't put a double-space after abbreviations like 'e.g.', 'i.e.'
  124.  #  
  125.  #  Works around the Alpha 'replaceText' bug.
  126.  # -------------------------------------------------------------------------
  127.  ##
  128. proc fillOneParagraph {{remember 1}} {
  129.     global leftFillColumn fillColumn doubleSpaces
  130.  
  131.     set pos [getPos]
  132.     
  133.     set start [paraStart $pos] 
  134.     set end [paraFinish $pos]
  135.     if $remember { rememberWhereYouAre $start $pos }
  136.  
  137.     # Get the leading whitespace of the current line and store length in 'left'
  138.     set front [getLeadingIndent $pos left]
  139.     # fill the text
  140.     regsub -all "\[ \t\r\]+" [string trim [getText $start $end]] " " text
  141.     # turn single spaces at end of sentences into double
  142.     if {$doubleSpaces} {regsub -all {(([^.][a-z]|[^a-zA-Z@]|\\@)[.?!]("|'|'')?([])])?) } $text {\1  } text}
  143. #     if {$doubleSpaces} {regsub -all {(([^A-Z@]|\\@)[.?!][])'"]?) } $text {\1  } text}
  144.  
  145.     # temporarily adjust the fillColumns
  146.     set ol $leftFillColumn
  147.     set or $fillColumn
  148.     set leftFillColumn 0
  149.     set fillColumn [expr $fillColumn - $left]
  150.         
  151.     # break and indent the paragraph
  152.     regsub -all "\r" "\r[string trimright [breakIntoLines $text]]" "\r${front}" text
  153.     # reset columns
  154.     set leftFillColumn $ol
  155.     set fillColumn  $or
  156.     
  157.     # don't replace if nothing's changed
  158.     if { "$text\r" != "\r[getText $start $end]" } {
  159.         # workaround an alpha bug
  160.         if $remember { 
  161.             getWinInfo a
  162.             if {[rowColToPos $a(currline) 0] > $start} { goto $start }
  163.         }
  164.         replaceText $start $end "[string range $text 1 end]\r"
  165.         if $remember {
  166.             goBackToWhereYouWere $start [expr $start + [string length $text]] 
  167.         }
  168.     }
  169.     
  170.     # in case we wish to fill a region
  171.     return $end
  172. }
  173.  
  174.  
  175. ## 
  176.  # -------------------------------------------------------------------------
  177.  # 
  178.  #    "paraStart"    -- "paraFinish"
  179.  # 
  180.  #     Newly simplified version with fewer regexp    '()' pairs.    Also I think
  181.  #     it    deals better with TeX comments than    the    old    regexp.
  182.  #     
  183.  #     "Start": It's pretty clear    for    non    TeX    modes how this works.  The only    
  184.  #     key is    that we    start at the beginning of the current line and look    back.  
  185.  #     We    then have a    quick check    for    whether    we found that very beginning (in 
  186.  #     which case    return it) or if not (in which case we have found the end of 
  187.  #     the previous paragraph) we move forward a line.
  188.  # 
  189.  #     "Finish": The only    addition is    the    need for an    additional check for
  190.  #     stuff which explicitly    ends lines.
  191.  #       
  192.  #    Results:
  193.  #     The start/finish position of the paragraph containing the given 'pos'
  194.  # 
  195.  # --Version--Author------------------Changes-------------------------------
  196.  #      1.1      <darley@fas.harvard.edu> Cut down on '()' pairs
  197.  #    1.2     Vince - March '96          Better filling for TeX tables ('hline')
  198.  #    1.3     Johan Linde - May '96   Now sensitive to HTML elements
  199.  #    1.4     <darley@fas.harvard.edu> Handle Tcl lists, top of file fix.
  200.  # -------------------------------------------------------------------------
  201.  ##
  202. proc paraStart {pos} {
  203.     global mode 
  204.     global ${mode}::startPara
  205.     if {$pos == [maxPos]} {incr pos -1}
  206.     set pos [lineStart $pos]
  207.     if [info exists ${mode}::startPara] {
  208.         set startPara [set ${mode}::startPara]
  209.     } else {
  210.         switch -- $mode {
  211.             "TeX" -
  212.             "Bib" {
  213.                 global texParaCommands
  214.                 set startPara {^[ \t]*$|\\\\[ \t]*$|(^|[^\\])%|\\h+line[ \t]*$|\$\$[ \t]*$|^[ \t]*(\\(}
  215.                 append startPara $texParaCommands {)(\[.*\]|\{.*\}|•)*[ \t]*)+$}
  216.             } 
  217.             "HTML" {
  218.                 global htmlParaCommands
  219.                 set startPara {^[ \t]*$|</?(}
  220.                 append startPara $htmlParaCommands {)([ \t\r]+[^>]*>|>)}
  221.             }
  222.             default {
  223.                 set startPara {^([ \t]*|([\\%].*))$}
  224.             }
  225.         }
  226.     }
  227.  
  228.     set res [search -s -n -f 0 -r 1 -l 0 "$startPara" $pos]
  229.     if {![string length $res] || $res == "0 0" } {
  230.         # bug work-around.  Alpha fails to match '^' with start of file.
  231.         return [lindex [search -s -f 1 -r 1 {[^ \t\r\n]} 0] 0]
  232.     } elseif { [lindex $res 0] == $pos } {
  233.         return $pos
  234.     } else {
  235.         return [nextLineStart [lindex $res 0]]
  236.     }
  237.     
  238. }
  239.  
  240. proc paraFinish {pos} {
  241.     global mode
  242.     global ${mode}::endPara
  243.     set pos [lineStart $pos]
  244.     set end [maxPos]
  245.     if [info exists ${mode}::endPara] {
  246.         set endPara [set ${mode}::endPara]
  247.     } else {
  248.         switch -- $mode {
  249.             "TeX" -
  250.             "Bib" {
  251.                 global texParaCommands
  252.                 set endPara {^[ \t]*$|(^|[^\\])%|\$\$[ \t]*$|^[ \t]*(\\(}
  253.                 append endPara $texParaCommands {)(\[.*\]|\{.*\}|•)*[ \t]*)+$}
  254.             } 
  255.             "HTML" {
  256.                 global htmlParaCommands
  257.                 set endPara {^[ \t]*$|</?(}
  258.                 append endPara $htmlParaCommands {)([ \t\r]+[^>]*>|>)}
  259.             }
  260.             default {
  261.                 set endPara {^([ \t]*|([\\%].*))$}
  262.             }
  263.         }
  264.     }
  265.     
  266.     set res [search -s -n -f 1 -r 1 -l $end "$endPara" $pos]
  267.     if {![string length $res]} {return $end}
  268.     set cpos [lineStart [lindex $res 0] ]
  269.     if { $cpos == $pos } {
  270.         return [nextLineStart $cpos]
  271.     }
  272.     # A line which ends in '\\', '%...', '\hline', '\hhline'
  273.     # signifies the end of the current paragraph in TeX mode
  274.     # (the above checked for beginning of the next paragraph).
  275.     if { $mode == "TeX" || $mode == "Bib" } {
  276.         set res2 [search -s -n -f 1 -r 1 -l $end {((\\\\|\\h+line)[ \t]*|[^\\]%.*)$} $pos]
  277.         if [string length $res2] {
  278.             if { [lindex $res2 0] < $cpos } {
  279.                 return [nextLineStart [lindex $res2 0]]
  280.             }
  281.         }
  282.     }
  283.  
  284.     return $cpos
  285.     
  286. }
  287.  
  288. proc selectParagraph {} {
  289.     set pos [getPos]
  290.     set start [paraStart $pos] 
  291.     set finish [paraFinish $pos]
  292.     goto $start
  293.     select $start $finish
  294. }
  295.  
  296. proc sentenceParagraph {} {
  297.     set pos [getPos]
  298.     set start [paraStart $pos] 
  299.     set finish [paraFinish $pos]
  300.  
  301.     set t [string trim [getText $start $finish]]
  302.     set period [regexp {\.$} $t]
  303.     regsub -all "\[ \t\r\]+" $t " " text
  304.     regsub -all {\. } $text "Δ" text
  305.     set result ""
  306.     foreach line [split [string trimright $text {.}] "Δ"] {
  307.         if {[string length $line]} {
  308.             append result [breakIntoLines $line] ".\r"
  309.         }
  310.     }
  311.     if {!$period && [regexp {\.\r} $result]} {
  312.         set result [string trimright $result ".\r"]
  313.         append result "\r"
  314.     }
  315.     if {$result != [getText $start $finish]} {
  316.         replaceText $start $finish $result
  317.     }
  318.     goto $pos
  319. }
  320.  
  321. proc getEndpts {} {
  322.     if {[getPos] == [selEnd]} {
  323.         set start [getPos]
  324.         set finish [getMark]
  325.         if {$start > $finish} {
  326.             set temp $start
  327.             set start $finish
  328.             set finish $temp
  329.         }
  330.     } else {
  331.         set start [getPos]
  332.         set finish [selEnd]
  333.     }
  334.     return [list $start $finish]
  335. }
  336.  
  337.  
  338. proc fillRegion {} {
  339.     global leftFillColumn
  340.     set ends [getEndpts]
  341.     set start [lineStart [lindex $ends 0]]
  342.     set finish [lindex $ends 1]
  343.     goto $start
  344.     set text [fillText $start $finish]
  345.     replaceText $start $finish [format "%$leftFillColumn\s" ""] $text "\r"
  346. }
  347.     
  348. proc wrapParagraph {} {
  349.     set pos [getPos]
  350.     set start [paraStart $pos] 
  351.     set finish [paraFinish $pos]
  352.     goto $start
  353.     wrapText $start $finish
  354.     goto $pos
  355. }
  356.  
  357. proc wrapRegion {} {
  358.     set ends [getEndpts]
  359.     set start [lineStart [lindex $ends 0]]
  360.     set finish [lindex $ends 1]
  361.     if {$start == $finish} {
  362.         set finish [maxPos]
  363.     }
  364.     wrapText $start $finish
  365. }
  366.     
  367.  
  368.  
  369. # Remove text from window, transform, and insert back into window.
  370. proc fillText {from to} {
  371.     global doubleSpaces
  372.     set text [getText $from $to]
  373.     regexp {^ *} $text front
  374.     set text [string trim $text]
  375.     regsub -all "\[ \t\r\]+" $text " " text
  376.     if {$doubleSpaces} {regsub -all {(\.|\?|\!) } $text {\1  } text}
  377.     regsub -all "\r" [string trimright [breakIntoLines $text]] "\r${front}" text
  378.     return $front$text
  379. }
  380.  
  381. proc paragraphToLine {} {
  382.     global fillColumn
  383.     global leftFillColumn
  384.     set fc $fillColumn
  385.     set lc $leftFillColumn
  386.     set fillColumn 10000
  387.     set leftFillColumn 0
  388.     fillRegion
  389.     set fillColumn $fc
  390.     set leftFillColumn $lc
  391. }
  392.  
  393. proc lineToParagraph {} {
  394.     global fillColumn
  395.     global leftFillColumn
  396.     set fc $fillColumn
  397.     set fillColumn 75
  398.     set lc $leftFillColumn
  399.     set leftFillColumn 0
  400.     fillRegion
  401.     set fillColumn $fc
  402.     set leftFillColumn $lc
  403. }
  404.  
  405.  
  406. #set sentEnd {[.!?](\r| +)}
  407. set sentEnd {(\r\r|[.!?](\r| +))}
  408. set sentBeg {[\r ][A-Z]}
  409.  
  410. proc nextSentence {} {
  411.     global sentBeg sentEnd
  412.     if {![catch {search -s -f 1 -r 1 $sentEnd [getPos]} mtch]} {
  413.         if {![catch {search -s -f 1 -r 1 -i 0 $sentBeg [expr [lindex $mtch 1]-1]} mtch]} {
  414.             goto [expr [lindex $mtch 0]+1]
  415.         }
  416.     }
  417. }
  418.  
  419.  
  420. proc prevSentence {} {
  421.     global sentBeg sentEnd
  422.     if {[catch {search -s -f 0 -r 1 $sentBeg [expr [getPos]-2]} mtch]} return
  423.     if {![catch {search -s -f 0 -r 1 $sentEnd [lindex $mtch 1]} mtch]} {
  424.         if {![catch {search -s -f 1 -r 1 -i 0 $sentBeg [expr [lindex $mtch 1]-1]} mtch]} {
  425.             goto [expr [lindex $mtch 0]+1]
  426.         }
  427.     }
  428. }
  429.  
  430. #===============================================================================
  431. # Called by Alpha to do "soft wrapping"
  432. proc softProc {pos start next} {
  433.     global leftFillColumn
  434.     goto $start
  435.     set finish [paraFinish $start]
  436.     set text [fillText $start $finish]
  437.     if {"${text}\r" != [getText $start $finish]} {
  438.         replaceText $start $finish [format "%$leftFillColumn\s" ""] $text "\r"
  439.         return 1
  440.     } else {
  441.         return 0
  442.     }
  443. }
  444.  
  445. proc dividingLine {} {
  446.     global mode
  447.     global ${mode}modeVars
  448.     if [info exists ${mode}modeVars(prefixString)] {
  449.         set a [string trim [set ${mode}modeVars(prefixString)]]
  450.     } else {
  451.         set a "#"
  452.     }
  453.     insertText "${a}===============================================================================\r"
  454. }
  455.  
  456.